package pers.cz.quartz.core;

import pers.cz.mapper.HostsAddressMapper;
import pers.cz.mapper.SystemJobMapper;
import pers.cz.mapper.TaskJobMapper;
import pers.cz.model.SystemJob;
import pers.cz.model.TaskJob;
import pers.cz.quartz.job.QuartzJobListener;
import pers.cz.spring.SpringContextUtil;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.GroupMatcher;
import org.quartz.impl.matchers.KeyMatcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;

import java.util.Date;
import java.util.List;

/**
 * @program: PostGirl-common
 * @description: QuartzJobFactory
 * @author: Cheng Zhi
 * @create: 2021-05-08 11:13
 **/
public class QuartzJobFactory {

    private static Logger logger= LoggerFactory.getLogger(QuartzJobFactory.class);
    private static Scheduler scheduler = null;
    private static final String CONFIG_NAME = "application.properties";
    private static final String GROUP_NAME = Scheduler.DEFAULT_GROUP;


    static {

        try {
            logger.info("开始加载定时任务");
            initJobs();
            logger.info("加载定时任务完成");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            logger.error("从数据库获取定时任务出错", e);
        } catch (SchedulerException e) {
            logger.error("加载定时任务出错", e);
        }
    }

    /**
     * 初始化job
     * @throws ClassNotFoundException
     * @throws SchedulerException
     */
    public static void initJobs() throws ClassNotFoundException, SchedulerException {

        logger.info("开始加载数据库中的定时任务");
        SystemJobMapper jobMapper = SpringContextUtil.getBean(SystemJobMapper.class);
        List<SystemJob> listJObs = jobMapper.selectAllJob();
        int count = 0;
        for (SystemJob job : listJObs) {
            count ++;
            // 如果加载的job中有开关关闭的需要置为失效
            TaskJobMapper taskJobMapper = SpringContextUtil.getBean(TaskJobMapper.class);
            TaskJob taskJob = taskJobMapper.selectByPrimaryKey(job.getJobId());
            job.setUserId(taskJob.getUserId());
            addJob(job);
            boolean jobSwitch = taskJob.getJobSwitch();
            if (!jobSwitch) {
                try {
                    pauserTrigger(job.getJobName());
                } catch (Exception e) {
                    logger.error("暂停定时任务失败", e);
                }
            }
        }
        logger.info("共加载[{}]条", count);

        if (scheduler != null) {
            scheduler.start();
        }

    }

    /**
     * 添加单个job
     * @param jobId
     * @param jobName
     * @param cron
     * @param jobStat
     * @param jobClass
     */
    public static void addJob(long jobId, String jobName, String triggerName, String cron, String jobStat, Class jobClass) throws SchedulerException, ClassNotFoundException {

        if (scheduler == null) {
            StdSchedulerFactory factory = new StdSchedulerFactory(CONFIG_NAME);
            scheduler = factory.getScheduler();
        }

        JobDetail jobDetail = JobBuilder.newJob(jobClass).build();
        CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(jobName, Scheduler.DEFAULT_GROUP).withSchedule(CronScheduleBuilder.cronSchedule(cron)).build();
        scheduler.scheduleJob(jobDetail, cronTrigger); // 注册

    }

    /**
     * 添加job
     * @param job
     * @throws SchedulerException
     * @throws ClassNotFoundException
     */
    public static void addJob(SystemJob job) {

        try {
            if (scheduler == null) {
                StdSchedulerFactory factory = new StdSchedulerFactory(CONFIG_NAME);
                scheduler = factory.getScheduler();
            }

            String className = job.getJobImplClass();

            ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
            Class<? extends Job> jobClass = (Class<? extends Job>) Class.forName(className);
            JobDetail jobDetail = JobBuilder.newJob(jobClass).withIdentity(job.getJobName(),GROUP_NAME).build();

            String param = job.getJobParam();
            String ext2 = job.getExt2();
            String url = "";
            Integer userId = job.getUserId();
            if (ext2 != null && !"".equals(ext2)) {
                url = SpringContextUtil.getBean(HostsAddressMapper.class).selectByPrimaryKey(Long.valueOf(ext2)).getHostAddress();
            }

            Long jobId = job.getJobId();
            String jobName = job.getJobName();
            Long timeOut = Long.valueOf(job.getExt1());
            QuartzJobListener listener = new QuartzJobListener(jobId, timeOut, jobName); // 定义job监听器用来监听job
            //scheduler.getListenerManager().addJobListener(listener); // 注册全局监听
            //JobKey jobKey = new JobKey(jobName, Scheduler.DEFAULT_GROUP);
            Matcher<JobKey> matcher = KeyMatcher.keyEquals(jobDetail.getKey()); // 为每个job增加一个监听器
            scheduler.getListenerManager().addJobListener(listener, matcher);
            if (logger.isDebugEnabled()) {
                logger.debug("添加的jobId为：{}", jobId);
                logger.debug("添加的jobName为：{}", jobName);
                logger.debug("添加的param为: {}", param);
                logger.debug("添加的url为: {}", url);
                logger.debug("添加的userId为:{}", userId);
            }
            jobDetail.getJobDataMap().put("param", param); // 这里传入自定义参数
            jobDetail.getJobDataMap().put("jobId", jobId);
            jobDetail.getJobDataMap().put("jobName", jobName);
            jobDetail.getJobDataMap().put("url", url);
            jobDetail.getJobDataMap().put("userId", userId);

            CronTrigger cronTrigger = TriggerBuilder.newTrigger().withIdentity(job.getJobName(), Scheduler.DEFAULT_GROUP).withSchedule(CronScheduleBuilder.cronSchedule(job.getCronExpression())).build();
            scheduler.scheduleJob(jobDetail, cronTrigger); // 注册
        } catch (Exception e) {
            logger.error("添加定时任务失败", e);
        }

    }

    /**
     * 启动job
     * @throws Exception
     */
    public static void startScheduler() throws Exception {
        if (scheduler != null) {
            scheduler.start();
        }
    }

    /**
     * 删除job
     */
    public static void removeJob(String jobName) throws Exception {
        if (scheduler == null) {
            SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = factory.getScheduler();
        }
        JobKey jobKey = new JobKey(jobName,GROUP_NAME);
        scheduler.deleteJob(JobKey.jobKey(jobName,GROUP_NAME));
    }

    /**
     * 暂停job
     * @param jobName
     * @throws SchedulerException
     */
    public static void pauserTrigger(String jobName) throws SchedulerException {

        if (logger.isDebugEnabled()) {
            logger.debug("暂停job:" + jobName);
        }

        if (scheduler == null) {
            SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = factory.getScheduler();
        }

        TriggerKey triggerKey = new TriggerKey(jobName);
        scheduler.pauseTrigger(triggerKey);
    }

    /**
     * 恢复job
     * @param jobName
     * @throws SchedulerException
     */
    public static void resumeTrigger(String jobName) throws SchedulerException {
        if (scheduler == null) {
            SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = factory.getScheduler();
        }

        TriggerKey triggerKey = new TriggerKey(jobName);
        scheduler.resumeTrigger(triggerKey);
    }

    /**
     * @Description: 立即运行任务
     * @param jobName
     */
    public static void triggerJob(String jobName) throws SchedulerException {

        if (scheduler == null) {
            SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = factory.getScheduler();
        }
        JobKey jobKey = JobKey.jobKey(jobName, GROUP_NAME);
        try {
            scheduler.triggerJob(jobKey);
        } catch (SchedulerException e) {
            logger.error("立即运行定时任务失败", e);
        }
    }

    /**
     * 动态修改执行计划
     * @param jobName
     * @param cron
     * @throws SchedulerException
     */
    public static void rescheduleJob(String jobName, String cron) throws SchedulerException {

        if (scheduler == null) {
            SchedulerFactory factory = new StdSchedulerFactory();
            Scheduler scheduler = factory.getScheduler();
        }

        // 获取trigger
        TriggerKey triggerKey = new TriggerKey(jobName);
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        String oldCronExpression = trigger.getCronExpression(); // 获取旧的表达式

        if (logger.isDebugEnabled()) {
            logger.debug("旧的执行计划为：{}", oldCronExpression);
            logger.debug("新的执行计划为：{}", cron);
        }
        // 将cron表达式进行转换
        CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
        // 创建触发器并将cron表达式对象给塞入
        trigger = TriggerBuilder.newTrigger().withIdentity(triggerKey).withSchedule(cronScheduleBuilder).build();
        // 重新布置任务
        scheduler.rescheduleJob(triggerKey,trigger);

    }
    /**
     * 获取触发器状态
     * NONE: 不存在
     * NORMAL: 正常
     * PAUSED: 暂停
     * COMPLETE:完成
     * ERROR : 错误
     * BLOCKED : 阻塞
     * @param jobName
     * @return
     */
    public static String getTriggerState(String jobName){
        TriggerKey triggerKey = TriggerKey.triggerKey(jobName, GROUP_NAME);
        String name = null;
        try {
            Trigger.TriggerState triggerState = scheduler.getTriggerState(triggerKey);
            name = triggerState.name();
        } catch (SchedulerException e) {
            logger.error("获取定时器状态失败", e);
        }
        return name;
    }

    /**
     * 打印所有由scheduler管理的job
     * @throws SchedulerException
     */
    public static void printAllJobName() throws SchedulerException {

        if (scheduler != null) {
            for (String groupName : scheduler.getJobGroupNames()) {

                for (JobKey jobKey : scheduler.getJobKeys(GroupMatcher.jobGroupEquals(groupName))) {

                    String jobName = jobKey.getName();
                    String jobGroup = jobKey.getGroup();

                    //get job's trigger
                    List<Trigger> triggers = (List<Trigger>) scheduler.getTriggersOfJob(jobKey);
                    Date nextFireTime = triggers.get(0).getNextFireTime();

                    System.out.println("[jobName] : " + jobName + " - [groupName] : "
                            + jobGroup + " - " + nextFireTime);

                }
            }
        }
    }

}

